#include "json.hpp"
#include "user.hpp"
#include "group.hpp"
#include "funs.hpp"

#include <iostream>
#include <arpa/inet.h>
#include "public.hpp"
#include <unistd.h>
#include <thread>
#include <chrono>

#include <semaphore.h>
#include <atomic>
using namespace std;
using json = nlohmann::json;

sem_t sem;
atomic_bool isLogin {false};
void hanldeLoginRes(json& res);
void hanleRegisterRes(json& res);
void readMsgTask(int cfd) {
    while (1) {
        char buff[1024]{0};
        int len = recv(cfd, buff, sizeof(buff), 0);
        if (len <= 0) {
            cout << "server close or error" << endl;
            continue;
        }
        // {"msgId": 2, "id": 22, "name": "Zhang San", "toId": 22, "msg": "hello world"}
        auto json = json::parse(buff);
        const EnMsHandler msgId = json["msgId"].get<EnMsHandler>();
        string name = json["name"].get<string>();
        printCurDateTime();
        if (msgId == EnMsHandler::SEND_MSG) {
            const int userId = json["userId"].get<int>();
            string msg = json["msg"].get<string>();
            cout << userId << " " << name << " say: " << msg << endl;
        } else if (msgId == EnMsHandler::GROUP_CHAT) {
            string msg = json["msg"].get<string>();
            const int groupId = json["groupId"].get<int>();
            cout << "Group " << groupId << " " << name << " say: " << msg << endl;
        } else if (msgId == EnMsHandler::LOGIN_ASK) {
            hanldeLoginRes(json);
            sem_post(&sem);
        } else if (msgId == EnMsHandler::REGISTER_ASK) {
            hanleRegisterRes(json);
            sem_post(&sem);
        }
    }
    
}

void loginHandler(int fd) {
    json req;
    req["msgId"] = EnMsHandler::LOGIN;
    int id = -1;
    string passwd = "";
    cout << "Please input your name: ";
    cin >> id;
    cin.get();
    cout << "Please input your password: ";
    getline(cin, passwd);
    req["id"] = id;
    req["password"] = passwd;
    auto msg = req.dump();
    send(fd, msg.c_str(), msg.size(), 0);
    
    // 专门开启一个线程用于接受发送来的消息
    static bool createThread = true;
    if (createThread) {
        createThread = false;
        thread t(readMsgTask, fd);
        t.detach();
    }
    sem_wait(&sem);
    if (isLogin) {
        // 登录之后要展示后续操作
        showMainMenu(fd);
    }
    return;
}
void registerHandler(int cfd) {
    json req;
    req["msgId"] = EnMsHandler::REGISTER;
    string name;
    string passwd;
    cout << "please input your username that you want to register: ";
    getline(cin,name);
    cin.get();
    cout << "please input your password: ";
    getline(cin,passwd);
    req["name"] = name;
    req["password"] = passwd;
    auto msg = req.dump();
    auto len = send(cfd, msg.c_str(), msg.size() + 1, 0);
    // 阻塞等待信号量的通知
    sem_wait(&sem);
}
int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket error");
        return 1;
    }
    sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8000);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect error");
        return 1;
    }
    sem_init(&sem, 0, 0);
    // 两个线程，一个负责接收消息，主线程发送消息
    while (true)
    {
        cout << "Please input your message: " << endl;
        cout << "============================= 1. Login =============================" << endl;
        cout << "============================= 2. Register =============================" << endl;
        cout << "============================= 3. Exit =============================" << endl;
        int choice;
        cin >> choice;
        cin.get();
        switch (choice)
        {
            case 1:
                loginHandler(sockfd);
                break;
            case 2:
                registerHandler(sockfd);
                break;
            case 3:
                sem_destroy(&sem);
                return 0;
            default:
                continue;
        }

    }
    
    return 0;
}
void hanldeLoginRes(json& res) {
    if (res["errno"].get<int>() != 0) {
        cout << res["errmsg"].get<string>() << endl;
        isLogin = false;
        return;
    }
    isLogin = true;
    // xxx 登录成功，保存用户信息
    const auto name = res["name"].get<string>();
    g_curUser.setId(res["id"].get<int>());
    g_curUser.setName(name);
    cout << "Login success, welcome " << name << endl;
    // 展示离线消息和好有列表 以及群消息
    // todo 用一个变量保存好友列表已经离线消息，之后统一格式化展示输出
    if (res.contains("friends")) {
        auto friends = res["friends"].get<vector<string>>();
        // cout << "firend name: \t" << "firend state:\t" << endl;
        for (const auto f : friends) {
            cout << f << endl;
        }
    }
    vector<Group> groupDatas;
    if (res.contains("groups")) {
        auto groups = res["groups"].get<vector<string>>();
        for (const auto g : groups) {
            auto groupjs = json::parse(g);
            Group group;
            group.SetId(groupjs["id"].get<int>());
            group.SetName(groupjs["name"].get<string>());
            group.SetDesc(groupjs["desc"].get<string>());
            vector<string> usersString = groupjs["users"].get<vector<string>>();
            for (const auto u : usersString) {
                auto userjs = json::parse(u);
                GroupUser gu;
                gu.setId(userjs["id"].get<int>());
                gu.setName(userjs["name"].get<string>());
                gu.setState(userjs["state"].get<string>());
                gu.setRole(userjs["role"].get<string>());
                group.GetUsers().push_back(gu);
            }
            groupDatas.push_back(group);
        }
        g_groups = std::move(groupDatas);
        showGroupsInfo();
    }
    if (res.contains("offlineMsg")) {
        auto offlineMsg = res["offlineMsg"].get<vector<string>>();
        for (const auto msg : offlineMsg) {
            cout << msg << endl;
        }
    }
}
void hanleRegisterRes(json& res) {
    if (res["errno"].get<int>() != 0) {
        cout << res["errmsg"].get<string>() << endl;
        return;
    }
}